package com.term.appadmin.shiro;

import com.term.domain.Employee;
import com.term.domain.Permission;
import com.term.domain.Role;
import com.term.service.IEmployeeService;
import com.term.service.IPermissionService;
import com.term.service.IRoleService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * Created by chenGang on 2017/1/16.
 */
public class ShiroRealm extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(ShiroRealm.class);

    @Resource
    private IRoleService roleService;

    @Resource
    private IEmployeeService employeeService;

    @Resource
    private IPermissionService permissionService;

    @Value(value = "${shiro.permission.debug:false}")
    private Boolean isDebug;

    @Value(value = "${shiro.permission.debug.companyid:10000}")
    private Long debugCompanyId;



    /**
     * 验证登录
     * @param authcToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        logger.info("doGetAuthenticationInfo ticket = " + token.getCredentials());


        if (isDebug) {
            return new SimpleAuthenticationInfo(new ShiroUser(1L, token.getUsername() , "super user", debugCompanyId)
                    , token.getCredentials(), "super user");
        }

        logger.info("doGetAuthenticationInfo Query Employee start");
        Optional<Employee> employeeOptional = employeeService.getEntityByField(Employee.class, "jobNo", Integer.valueOf(token.getUsername()));
        logger.info("doGetAuthenticationInfo Query Employee end");

        if (!employeeOptional.isPresent()) {
            logger.info("Employee not exist");
            return null;
        }
        Employee employee = employeeOptional.get();
        return new SimpleAuthenticationInfo(new ShiroUser(employee.getId(), String.valueOf(employee.getJobNo())
                , employee.getName(), employee.getCompany().getId())
                , token.getCredentials(), employee.getName());
    }

    /**
     * 权限初始化
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("doGetAuthorizationInfo start");
        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();

        logger.info("doGetAuthorizationInfo query Employee start");
        Optional<Employee> employeeOptional = employeeService.getEntityByField(Employee.class, "jobNo", Integer.valueOf(shiroUser.getJobNo()));
        logger.info("doGetAuthorizationInfo query Employee End");

        if (!isDebug) {
            Employee employee = employeeOptional.get();
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addRole(employee.getRole().getName());
            logger.info("doGetAuthorizationInfo query Role start");
            Optional<Role> role = roleService.getEntityById(Role.class, employee.getRole().getId());
            logger.info("doGetAuthorizationInfo query Role End");
            if ((!role.isPresent()) || role.get().getPermissionList() == null || role.get().getPermissionList().isEmpty()) {
                return info;
            }

            for (Permission permission : role.get().getPermissionList()) {
                if (permission != null) {
                    info.addStringPermission(permission.getCode());
                }
            }
            logger.info("doGetAuthorizationInfo end");
            return info;
        } else {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addRole("super user");

            List<Permission> permissionList = permissionService.getAllEntities(Permission.class);
            for (Permission permission : permissionList) {
                info.addStringPermission(permission.getCode());
            }
            return info;
        }
    }

    public class ShiroUser implements Serializable {
        private static final long serialVersionUID = -1373760761780840081L;
        private Long id;
        private String jobNo;
        private String name;
        private Long companyId;

        public ShiroUser(Long id, String jobNo, String name, Long companyId) {
            this.id = id;
            this.jobNo = jobNo;
            this.name = name;
            this.companyId = companyId;
        }

        public String getName() {
            return name;
        }

        public Long getId() {
            return id;
        }

        public String getJobNo() {
            return jobNo;
        }

        public Long getCompanyId() {
            return companyId;
        }

        /**
         * 本函数输出将作为默认的<shiro:principal/>输出.
         */
        @Override
        public String toString() {
            return name;
        }

        /**
         * 重载hashCode,只计算jobNo;
         */
        @Override
        public int hashCode() {
            return Objects.hashCode(jobNo);
        }

        /**
         * 重载equals,只计算jobNo;
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            ShiroUser other = (ShiroUser) obj;
            if (jobNo == null) {
                if (other.jobNo != null) {
                    return false;
                }
            } else if (!jobNo.equals(other.jobNo)) {
                return false;
            }
            return true;
        }
    }

}
